/*
 * Copyright (C) 2012, EADS France
 * 
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 * 
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 */
package xowl.utils;

import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.io.Reader;
import java.io.Writer;
import java.nio.charset.Charset;

/**
 *
 * @author Laurent WOUTERS
 */
public class Files {
    private static final int bufferSize = 1024;
    
    private static final int[] bomUTF8 = new int[] { 0xEF, 0xBB, 0xBF };
    private static final int[] bomUTF16LE = new int[] { 0xFF, 0xFE };
    private static final int[] bomUTF16BE = new int[] { 0xFE, 0xFF };
    private static final int[] bomUTF32LE = new int[] { 0xFF, 0xFE, 0x00, 0x00 };
    private static final int[] bomUTF32BE = new int[] { 0x00, 0x00, 0xFE, 0xFF };
    
    public static Reader getReader(String file) throws IOException {
        Charset charset = detectEncoding(file);
        InputStream stream = new java.io.FileInputStream(file);
        int[] overflow = null;
        if (charset.name().equals("UTF-8")) overflow = strip(stream, bomUTF8);
        else if (charset.name().equals("UTF-16LE")) overflow = strip(stream, bomUTF16LE);
        else if (charset.name().equals("UTF-16BE")) overflow = strip(stream, bomUTF16BE);
        else if (charset.name().equals("UTF-32LE")) overflow = strip(stream, bomUTF32LE);
        else if (charset.name().equals("UTF-32BE")) overflow = strip(stream, bomUTF32BE);
        if (overflow != null)
            stream = new CompositeInputStream(overflow, stream);
        return new InputStreamReader(stream, charset);
    }
    
    public static Writer getWriter(String file) throws IOException {
        return new java.io.OutputStreamWriter(new FileOutputStream(file), "UTF-8");
    }
    
    public static String read(InputStream stream, Charset charset) throws IOException {
        return read(new InputStreamReader(stream, charset));
    }
    
    public static String read(Reader reader) throws IOException {
        StringBuilder fileData = new StringBuilder(1000);
        char[] buf = new char[bufferSize];
        int numRead=0;
        while((numRead=reader.read(buf)) != -1){
            fileData.append(buf, 0, numRead);
        }
        reader.close();
        return fileData.toString();
    }
    
    public static Charset detectEncoding(String file) throws IOException {
        FileInputStream stream = new FileInputStream(file);
        try {
            int b0 = stream.read();
            int b1 = stream.read();
            if (b0 == -1 || b1 == -1) {
                stream.close();
                return Charset.forName("UTF-8");
            }
            if (b0 == bomUTF16BE[0] && b1 == bomUTF16BE[1]) {
                stream.close();
                return Charset.forName("UTF-16BE");
            }
            int b2 = stream.read();
            if (b2 == -1) {
                stream.close();
                if (b0 == bomUTF16LE[0] && b1 == bomUTF16LE[1])  return Charset.forName("UTF-16LE");
                return Charset.forName("UTF-8");
            }
            if (b0 == bomUTF8[0] && b1 == bomUTF8[1] && b2 == bomUTF8[2]) {
                stream.close();
                return Charset.forName("UTF-8");
            }
            int b3 = stream.read();
            if (b3 == -1) {
                stream.close();
                if (b0 == bomUTF16LE[0] && b1 == bomUTF16LE[1]) return Charset.forName("UTF-16LE");
                return Charset.forName("UTF-8");
            }
            
            if (b0 == bomUTF32BE[0] && b1 == bomUTF32BE[1] && b2 == bomUTF32BE[2] && b3 == bomUTF32BE[3]) return Charset.forName("UTF-32BE");
            if (b0 == bomUTF32LE[0] && b1 == bomUTF32LE[1] && b2 == bomUTF32LE[2] && b3 == bomUTF32LE[3]) return Charset.forName("UTF-32LE");
            return Charset.forName("UTF-8");
        }
        finally { stream.close(); }
    }
    
    private static int[] strip(InputStream stream, int[] bom) throws IOException {
        int[] overflow = new int[bom.length];
        for (int i=0; i!=bom.length; i++) {
            int value = stream.read();
            overflow[i] = value;
            if (value != bom[i])
                return overflow;
        }
        return null;
    }
}
